From f478d9f0f61d46102507a034f63919a5c9cb0e04 Mon Sep 17 00:00:00 2001 From: Xan Lopez Date: Fri, 27 Apr 2007 16:50:04 +0000 Subject: [PATCH] Support inline-selection in entries (#318459) Support inline-selection in entries (#318459) * gtk/gtkentry.c: * gtk/gtkentrycompletion.c: * gtk/gtkentrycompletion.h: * gtk/gtkentryprivate.h: When enabled cursor-match is emited when the cursor is on a possible completion on the list. The default implementation will replace the contents on the entry with the contents of the text column in the completion model. Review and improvements by Matthias Clasen. svn path=/trunk/; revision=17660 --- ChangeLog | 16 ++++ gtk/gtkentry.c | 43 ++++++++++- gtk/gtkentrycompletion.c | 154 ++++++++++++++++++++++++++++++++++++++- gtk/gtkentrycompletion.h | 10 ++- gtk/gtkentryprivate.h | 2 + 5 files changed, 216 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index c52305a947..a4cb1f1f48 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2007-04-27 Xan Lopez + + Support inline-selection in entries (#318459) + + * gtk/gtkentry.c: + * gtk/gtkentrycompletion.c: + * gtk/gtkentrycompletion.h: + * gtk/gtkentryprivate.h: + + When enabled cursor-match is emited when the cursor is on + a possible completion on the list. The default implementation + will replace the contents on the entry with the contents of + the text column in the completion model. + + Review and improvements by Matthias Clasen. + 2007-04-27 Michael Natterer Merged heavily modified patch from maemo-gtk which enables opening diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index e6653d1a0d..992ac6f00b 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -5679,6 +5679,22 @@ gtk_entry_completion_key_press (GtkWidget *widget, path = gtk_tree_path_new_from_indices (completion->priv->current_selected, -1); gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view), path, NULL, FALSE); + + if (completion->priv->inline_selection) + { + + GtkTreeIter iter; + GtkTreeModel *model = NULL; + GtkTreeSelection *sel; + gboolean entry_set; + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)); + if (!gtk_tree_selection_get_selected (sel, &model, &iter)) + return FALSE; + + g_signal_emit_by_name (completion, "cursor_on_match", model, + &iter, &entry_set); + } } else if (completion->priv->current_selected - matches >= 0) { @@ -5693,11 +5709,23 @@ gtk_entry_completion_key_press (GtkWidget *widget, return TRUE; } - else if (event->keyval == GDK_Escape) + else if (event->keyval == GDK_Escape || + event->keyval == GDK_Left || + event->keyval == GDK_KP_Left || + event->keyval == GDK_Right || + event->keyval == GDK_KP_Right) { _gtk_entry_reset_im_context (GTK_ENTRY (widget)); _gtk_entry_completion_popdown (completion); + if (completion->priv->inline_selection) + { + if (event->keyval == GDK_Escape) + gtk_editable_delete_selection (GTK_EDITABLE (widget)); + /* Move the cursor to the end */ + gtk_editable_set_position (GTK_EDITABLE (widget), -1); + } + return TRUE; } else if (event->keyval == GDK_Tab || @@ -5750,7 +5778,7 @@ gtk_entry_completion_key_press (GtkWidget *widget, /* move the cursor to the end */ gtk_editable_set_position (GTK_EDITABLE (widget), -1); - g_free (str); + g_free (str); } return TRUE; @@ -5880,12 +5908,18 @@ disconnect_completion_signals (GtkEntry *entry, G_CALLBACK (completion_changed), entry); if (completion->priv->changed_id > 0 && g_signal_handler_is_connected (entry, completion->priv->changed_id)) - g_signal_handler_disconnect (entry, completion->priv->changed_id); + { + g_signal_handler_disconnect (entry, completion->priv->changed_id); + completion->priv->changed_id = 0; + } g_signal_handlers_disconnect_by_func (entry, G_CALLBACK (gtk_entry_completion_key_press), completion); if (completion->priv->insert_text_id > 0 && g_signal_handler_is_connected (entry, completion->priv->insert_text_id)) - g_signal_handler_disconnect (entry, completion->priv->insert_text_id); + { + g_signal_handler_disconnect (entry, completion->priv->insert_text_id); + completion->priv->insert_text_id = 0; + } g_signal_handlers_disconnect_by_func (entry, G_CALLBACK (completion_insert_text_callback), completion); g_signal_handlers_disconnect_by_func (entry, @@ -5919,6 +5953,7 @@ connect_completion_signals (GtkEntry *entry, g_signal_connect (entry, "focus_out_event", G_CALLBACK (accept_completion_callback), completion); } + g_signal_connect (completion, "notify", G_CALLBACK (completion_changed), entry); } diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c index d8bc08a06a..f4d74d62f4 100644 --- a/gtk/gtkentrycompletion.c +++ b/gtk/gtkentrycompletion.c @@ -47,6 +47,7 @@ enum INSERT_PREFIX, MATCH_SELECTED, ACTION_ACTIVATED, + CURSOR_ON_MATCH, LAST_SIGNAL }; @@ -60,7 +61,8 @@ enum PROP_INLINE_COMPLETION, PROP_POPUP_COMPLETION, PROP_POPUP_SET_WIDTH, - PROP_POPUP_SINGLE_MATCH + PROP_POPUP_SINGLE_MATCH, + PROP_INLINE_SELECTION }; #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate)) @@ -136,6 +138,9 @@ static gboolean gtk_entry_completion_match_selected (GtkEntryCompletion *co GtkTreeIter *iter); static gboolean gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion, const gchar *prefix); +static gboolean gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter); static guint entry_completion_signals[LAST_SIGNAL] = { 0 }; @@ -156,6 +161,7 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) klass->match_selected = gtk_entry_completion_match_selected; klass->insert_prefix = gtk_entry_completion_real_insert_prefix; + klass->cursor_on_match = gtk_entry_completion_cursor_on_match; /** * GtkEntryCompletion::insert-prefix: @@ -210,6 +216,32 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) G_TYPE_BOOLEAN, 2, GTK_TYPE_TREE_MODEL, GTK_TYPE_TREE_ITER); + /** + * GtkEntryCompletion::cursor-on-match: + * @widget: the object which received the signal + * @model: the #GtkTreeModel containing the matches + * @iter: a #GtkTreeIter positioned at the selected match + * + * Gets emitted when a match from the cursor is on a match + * of the list.The default behaviour is to replace the contents + * of the entry with the contents of the text column in the row + * pointed to by @iter. + * + * Return value: %TRUE if the signal has been handled + * + * Since: 2.12 + */ + + entry_completion_signals[CURSOR_ON_MATCH] = + g_signal_new (I_("cursor_on_match"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkEntryCompletionClass, cursor_on_match), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__OBJECT_BOXED, + G_TYPE_BOOLEAN, 2, + GTK_TYPE_TREE_MODEL, + GTK_TYPE_TREE_ITER); /** * GtkEntryCompletion::action-activated: @@ -330,6 +362,21 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass) P_("If TRUE, the popup window will appear for a single match."), TRUE, GTK_PARAM_READWRITE)); + /** + * GtkEntryCompletion:inline-selection: + * + * Determines whether the possible completions on the popup + * will appear in the entry as you navigate through them. + + * Since: 2.12 + */ + g_object_class_install_property (object_class, + PROP_INLINE_SELECTION, + g_param_spec_boolean ("inline-selection", + P_("Inline selection"), + P_("Your description here"), + FALSE, + GTK_PARAM_READWRITE)); g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate)); } @@ -364,6 +411,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion) priv->popup_completion = TRUE; priv->popup_set_width = TRUE; priv->popup_single_match = TRUE; + priv->inline_selection = FALSE; /* completions */ priv->filter_model = NULL; @@ -378,6 +426,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion) g_signal_connect (priv->tree_view, "motion_notify_event", G_CALLBACK (gtk_entry_completion_list_motion_notify), completion); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE); gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE); @@ -505,6 +554,10 @@ gtk_entry_completion_set_property (GObject *object, priv->popup_single_match = g_value_get_boolean (value); break; + case PROP_INLINE_SELECTION: + priv->inline_selection = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -550,6 +603,10 @@ gtk_entry_completion_get_property (GObject *object, g_value_set_boolean (value, gtk_entry_completion_get_popup_single_match (completion)); break; + case PROP_INLINE_SELECTION: + g_value_set_boolean (value, gtk_entry_completion_get_inline_selection (completion)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1277,7 +1334,7 @@ gtk_entry_completion_list_motion_notify (GtkWidget *widget, { GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data); - completion->priv->ignore_enter = FALSE; + completion->priv->ignore_enter = FALSE; return FALSE; } @@ -1468,6 +1525,15 @@ gtk_entry_completion_match_selected (GtkEntryCompletion *completion, return TRUE; } +static gboolean +gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + gtk_entry_completion_insert_completion (completion, model, iter); + + return TRUE; +} static gchar * gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion) @@ -1565,6 +1631,65 @@ gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion, return TRUE; } +void +gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion, + const gchar *text) +{ + GtkEntryCompletionPrivate *priv = completion->priv; + gint len; + + if (priv->changed_id > 0) + { + g_signal_handler_block (priv->entry, + priv->changed_id); + } + + if (priv->insert_text_id > 0) + { + g_signal_handler_block (completion->priv->entry, + completion->priv->insert_text_id); + } + + gtk_editable_get_selection_bounds (GTK_EDITABLE (priv->entry), &len, NULL); + gtk_entry_set_text (GTK_ENTRY (priv->entry), text); + gtk_editable_select_region (GTK_EDITABLE (priv->entry), len, -1); + + if (priv->changed_id > 0) + { + g_signal_handler_unblock (priv->entry, + priv->changed_id); + } + + if (priv->insert_text_id > 0) + { + g_signal_handler_unblock (priv->entry, + priv->insert_text_id); + } +} + +gboolean +gtk_entry_completion_insert_completion (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + gchar *str = NULL; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE); + + if (completion->priv->text_column < 0) + return FALSE; + + gtk_tree_model_get (model, iter, + completion->priv->text_column, &str, + -1); + + gtk_entry_completion_insert_completion_text (completion, str); + + g_free (str); + + return TRUE; +} + /** * gtk_entry_completion_insert_prefix: * @completion: a #GtkEntryCompletion @@ -1573,6 +1698,7 @@ gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion, * * Since: 2.6 **/ + void gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion) { @@ -1622,7 +1748,6 @@ gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion, } } - /** * gtk_entry_completion_get_inline_completion: * @completion: a #GtkEntryCompletion @@ -1782,6 +1907,29 @@ gtk_entry_completion_get_popup_single_match (GtkEntryCompletion *completion) return completion->priv->popup_single_match; } +void +gtk_entry_completion_set_inline_selection (GtkEntryCompletion *completion, + gboolean inline_selection) +{ + g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion)); + + inline_selection = inline_selection != FALSE; + + if (completion->priv->inline_selection != inline_selection) + { + completion->priv->inline_selection = inline_selection; + + g_object_notify (G_OBJECT (completion), "inline-selection"); + } +} + +gboolean +gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion) +{ + g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE); + + return completion->priv->inline_selection; +} #define __GTK_ENTRY_COMPLETION_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkentrycompletion.h b/gtk/gtkentrycompletion.h index 01a40fd957..26285d2ac6 100644 --- a/gtk/gtkentrycompletion.h +++ b/gtk/gtkentrycompletion.h @@ -65,11 +65,13 @@ struct _GtkEntryCompletionClass gint index_); gboolean (* insert_prefix) (GtkEntryCompletion *completion, const gchar *prefix); + gboolean (* cursor_on_match) (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter); /* Padding for future expansion */ void (*_gtk_reserved0) (void); void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); }; /* core */ @@ -104,6 +106,9 @@ void gtk_entry_completion_delete_action (GtkEntryComplet void gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion, gboolean inline_completion); gboolean gtk_entry_completion_get_inline_completion (GtkEntryCompletion *completion); +void gtk_entry_completion_set_inline_selection (GtkEntryCompletion *completion, + gboolean inline_selection); +gboolean gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion); void gtk_entry_completion_set_popup_completion (GtkEntryCompletion *completion, gboolean popup_completion); gboolean gtk_entry_completion_get_popup_completion (GtkEntryCompletion *completion); @@ -114,7 +119,8 @@ void gtk_entry_completion_set_popup_single_match (GtkEntryComplet gboolean popup_single_match); gboolean gtk_entry_completion_get_popup_single_match (GtkEntryCompletion *completion); - +void gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion, + const gchar *text); /* convenience */ void gtk_entry_completion_set_text_column (GtkEntryCompletion *completion, gint column); diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h index 6589a0c201..696f744401 100644 --- a/gtk/gtkentryprivate.h +++ b/gtk/gtkentryprivate.h @@ -64,6 +64,8 @@ struct _GtkEntryCompletionPrivate guint popup_completion : 1; guint popup_set_width : 1; guint popup_single_match : 1; + guint inline_selection : 1; + GSource *check_completion_idle; }; -- 2.30.2